LÄs upp exakt kontroll över JavaScript-modulupplösning med Import Maps. Denna omfattande guide utforskar deras fördelar, implementering och inverkan pÄ modern, global webbutveckling.
JavaScript Import Maps: BemÀstra kontrollen över modulupplösning för global utveckling
I det stÀndigt förÀnderliga landskapet för JavaScript-utveckling Àr det av största vikt att hantera beroenden och sÀkerstÀlla förutsÀgbar modulladdning. NÀr applikationer vÀxer i komplexitet och global rÀckvidd blir behovet av detaljerad kontroll över hur JavaScript-moduler löses alltmer kritiskt. HÀr kommer JavaScript Import Maps in i bilden, ett kraftfullt webblÀsar-API som ger utvecklare enastÄende kontroll över modulupplösning och erbjuder en strömlinjeformad och robust metod för beroendehantering.
Denna omfattande guide kommer att djupdyka i JavaScript Import Maps, utforska deras grundlÀggande koncept, fördelar, praktisk implementering och den betydande inverkan de kan ha pÄ dina globala webbutvecklingsprojekt. Vi kommer att navigera genom olika scenarier, ge handlingskraftiga insikter och belysa hur Import Maps kan förbÀttra prestanda, förenkla arbetsflöden och frÀmja större interoperabilitet över olika utvecklingsmiljöer.
Utvecklingen av JavaScript-moduler och behovet av upplösningskontroll
Innan vi dyker in i Import Maps Àr det viktigt att förstÄ resan för JavaScript-moduler. Historiskt sett saknade JavaScript ett standardiserat modulsystem, vilket ledde till olika ad hoc-lösningar som CommonJS (anvÀnds flitigt i Node.js) och AMD (Asynchronous Module Definition). Dessa system, Àven om de var effektiva pÄ sin tid, innebar utmaningar vid övergÄngen till ett webblÀsarnativt modulsystem.
Introduktionen av ES-moduler (ECMAScript Modules) med syntaxen import
och export
markerade ett betydande framsteg och införde ett standardiserat, deklarativt sÀtt att organisera och dela kod. Den förvalda upplösningsmekanismen för ES-moduler i webblÀsare och Node.js, Àven om den Àr funktionell, kan dock ibland vara ogenomskinlig eller leda till oavsiktliga konsekvenser, sÀrskilt i stora, distribuerade team som arbetar i olika regioner och med varierande utvecklingsuppsÀttningar.
TÀnk pÄ ett scenario dÀr ett globalt team arbetar pÄ en stor e-handelsplattform. Olika team kan vara ansvariga för olika funktioner, dÀr var och en förlitar sig pÄ en gemensam uppsÀttning bibliotek. Utan ett tydligt och kontrollerbart sÀtt att specificera modulplatser kan utvecklare stöta pÄ:
- Versionskonflikter: Olika delar av applikationen drar oavsiktligt in olika versioner av samma bibliotek.
- Beroendehelvete (Dependency Hell): Komplexa ömsesidiga beroenden som Àr svÄra att reda ut och hantera.
- Redundanta nedladdningar: Samma modul hÀmtas flera gÄnger frÄn olika sökvÀgar.
- Komplexitet i byggverktyg: Att förlita sig starkt pÄ bundlers som Webpack eller Rollup för att hantera upplösning, vilket ökar byggkomplexiteten och potentiellt saktar ner utvecklingscyklerna.
Det Àr precis hÀr Import Maps briljerar. De erbjuder ett deklarativt sÀtt att mappa "nakna" modulspecificerare (som 'react'
eller 'lodash'
) till faktiska URL:er eller sökvÀgar, vilket ger utvecklare explicit kontroll över upplösningsprocessen.
Vad Àr JavaScript Import Maps?
I grund och botten Àr en Import Map ett JSON-objekt som tillhandahÄller en uppsÀttning regler för hur JavaScript-runtime ska lösa modulspecificerare. Det lÄter dig:
- Mappa "nakna" specificerare till URL:er: IstÀllet för att skriva
import React from './node_modules/react/index.js'
, kan du skrivaimport React from 'react'
och lÄta din Import Map specificera att'react'
ska lösas till en specifik CDN-URL eller en lokal sökvÀg. - Skapa alias: Definiera anpassade alias för moduler, vilket gör dina import-satser renare och mer underhÄllbara.
- Hantera olika versioner: Potentiellt vÀxla mellan olika versioner av ett bibliotek baserat pÄ miljön eller specifika behov, utan att Àndra dina import-satser.
- Kontrollera modulladdningsbeteende: PÄverka hur moduler laddas, vilket kan ha prestandakonsekvenser.
Import Maps definieras vanligtvis inom en <script type="importmap">
-tagg i din HTML eller laddas som en separat JSON-fil. WebblÀsaren eller Node.js-miljön anvÀnder sedan denna karta för att lösa alla import
- eller export
-satser i dina JavaScript-moduler.
Strukturen för en Import Map
En Import Map Àr ett JSON-objekt med en specifik struktur:
{
"imports": {
"react": "/modules/react.js",
"lodash": "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.js"
}
}
LÄt oss bryta ner nyckelkomponenterna:
imports
: Detta Àr den primÀra nyckeln för att definiera modulmappningar. Den innehÄller ett nÀstlat JSON-objekt dÀr nycklarna Àr modulspecificerare (det du skulle anvÀnda i dinimport
-sats) och vÀrdena Àr motsvarande modul-URL:er eller sökvÀgar.- "Nakna" specificerare (Bare Specifiers): Nycklar som
"react"
eller"lodash"
Àr kÀnda som "nakna" specificerare. Dessa Àr de icke-relativa, icke-absoluta strÀngar som ofta kommer frÄn pakethanterare. - Modul-URL:er/sökvÀgar: VÀrden som
"/modules/react.js"
eller"https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.js"
Àr de faktiska platserna dÀr JavaScript-modulerna kan hittas. Dessa kan vara relativa sökvÀgar, absoluta sökvÀgar eller URL:er som pekar till CDN:er eller andra externa resurser.
Avancerade funktioner i Import Maps
Import Maps erbjuder mer sofistikerade funktioner utöver grundlÀggande mappningar:
1. OmfÄng (Scopes)
Egenskapen scopes
lÄter dig definiera olika upplösningsregler för olika moduler. Detta Àr otroligt anvÀndbart för att hantera beroenden inom specifika delar av din applikation eller för att hantera situationer dÀr ett bibliotek kan ha sina egna interna modulupplösningsbehov.
TÀnk pÄ ett scenario dÀr du har en kÀrnapplikation och en uppsÀttning plugins. Varje plugin kan förlita sig pÄ en specifik version av ett delat bibliotek, medan kÀrnapplikationen anvÀnder en annan version. Med omfÄng kan du hantera detta:
{
"imports": {
"utils": "/core/utils.js"
},
"scopes": {
"/plugins/pluginA/": {
"shared-lib": "/node_modules/shared-lib/v1/index.js"
},
"/plugins/pluginB/": {
"shared-lib": "/node_modules/shared-lib/v2/index.js"
}
}
}
I detta exempel:
- Varje modul som laddas frÄn katalogen
/plugins/pluginA/
och som importerar"shared-lib"
kommer att lösas till"/node_modules/shared-lib/v1/index.js"
. - PÄ samma sÀtt kommer moduler frÄn
/plugins/pluginB/
som importerar"shared-lib"
att anvÀnda version 2. - Alla andra moduler (som inte Àr explicit inom ett omfÄng) kommer att anvÀnda den globala
"utils"
-mappningen.
Denna funktion Àr sÀrskilt kraftfull för att bygga modulÀra, utbyggbara applikationer, speciellt i företagsmiljöer med komplexa, mÄngfacetterade kodbaser.
2. Paketidentifierare (Prefix-fallbacks)
Import Maps stöder ocksÄ mappning av prefix, vilket gör att du kan definiera en standardupplösning för alla moduler som börjar med ett visst paketnamn. Detta anvÀnds ofta för att mappa paketnamn frÄn ett CDN till deras faktiska platser.
{
"imports": {
"lodash": "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.js",
"@fortawesome/fontawesome-free/": "https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.1.1/",
"./": "/src/"
}
}
I detta exempel:
"lodash"
mappas till sin specifika CDN-URL."@fortawesome/fontawesome-free/"
mappas till bas-URL:en för det paketet. NÀr du importerar"@fortawesome/fontawesome-free/svg-core"
kommer det att lösas till"https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.1.1/svg-core"
. Det avslutande snedstrecket Àr avgörande hÀr."./"
mappas till"/src/"
. Detta innebÀr att varje relativ import som börjar med"./"
nu kommer att prefixas med"/src/"
. Till exempel skulleimport './components/Button'
i praktiken försöka ladda/src/components/Button.js
.
Denna prefixmappning Àr ett mer flexibelt sÀtt att hantera moduler frÄn npm-paket eller lokala katalogstrukturer utan att behöva mappa varje enskild fil.
3. SjÀlvrefererande moduler
Import Maps tillÄter moduler att referera till sig sjÀlva med sin "nakna" specificerare. Detta Àr anvÀndbart nÀr en modul behöver importera andra moduler frÄn samma paket.
{
"imports": {
"my-library": "/node_modules/my-library/index.js"
}
}
Inom my-library
s kod kan du nu göra:
import { helper } from 'my-library/helpers';
// Detta kommer att lösas korrekt till /node_modules/my-library/helpers.js
Hur man anvÀnder Import Maps
Det finns tvÄ primÀra sÀtt att introducera en Import Map i din applikation:
1. Inline i HTML
Den mest direkta metoden Àr att bÀdda in din Import Map direkt i en <script type="importmap">
-tagg i din HTML-fil:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Import Map Exempel</title>
<script type="importmap">
{
"imports": {
"react": "https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.production.min.js",
"react-dom": "https://cdn.jsdelivr.net/npm/react-dom@18.2.0/umd/react-dom.production.min.js"
}
}
</script>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/app.js"></script>
</body>
</html>
I /src/app.js
:
import React from 'react';
import ReactDOM from 'react-dom';
function App() {
return React.createElement('h1', null, 'Hej frÄn React!');
}
ReactDOM.render(React.createElement(App), document.getElementById('root'));
NÀr webblÀsaren stöter pÄ <script type="module" src="/src/app.js">
kommer den att bearbeta alla importer inom app.js
med hjÀlp av den definierade Import Map.
2. Extern Import Map JSON-fil
För bÀttre organisation, sÀrskilt i större projekt eller nÀr man hanterar flera importkartor, kan du lÀnka till en extern JSON-fil:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Extern Import Map Exempel</title>
<script type="importmap" src="/import-maps.json"></script>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/app.js"></script>
</body>
</html>
Och filen /import-maps.json
skulle innehÄlla:
{
"imports": {
"axios": "https://cdn.jsdelivr.net/npm/axios@1.4.0/dist/axios.min.js",
"./utils/": "/src/utils/"
}
}
Detta tillvÀgagÄngssÀtt hÄller din HTML renare och gör att din importkarta kan cachas separat.
WebblÀsarstöd och övervÀganden
Import Maps Àr en relativt ny webbstandard, och Àven om webblÀsarstödet vÀxer Àr det Ànnu inte universellt. Vid min senaste uppdatering erbjuder stora webblÀsare som Chrome, Edge och Firefox stöd, ofta initialt bakom funktionsflaggor. Safaris stöd fortsÀtter ocksÄ att utvecklas.
För globala mÄlgrupper och bredare kompatibilitet, övervÀg följande:
- Funktionsdetektering: Du kan detektera om Import Maps stöds med JavaScript innan du försöker förlita dig pÄ dem.
- Polyfills: Ăven om en sann polyfill för webblĂ€sarens inbyggda Import Map-upplösning Ă€r komplex, kan verktyg som es-module-shims tillhandahĂ„lla en shim för ES-modulladdning i webblĂ€sare som inte har inbyggt stöd, och vissa av dessa shims kan ocksĂ„ utnyttja importkartor.
- Byggverktyg: Ăven med Import Maps förblir byggverktyg som Vite, Webpack eller Rollup nödvĂ€ndiga för mĂ„nga utvecklingsflöden. De kan ofta konfigureras för att fungera tillsammans med eller till och med generera importkartor. Till exempel kan verktyg som Vite utnyttja importkartor för förhandsbuntning av beroenden, vilket leder till snabbare kallstarter.
- Node.js-stöd: Node.js har ocksÄ experimentellt stöd för Import Maps, styrt via flaggorna
--experimental-specifier-resolution=node --experimental-import-maps
eller genom att sÀtta"type": "module"
i dinpackage.json
och anvÀnda kommandotnode --import-maps=import-maps.json
. Detta möjliggör en konsekvent upplösningsstrategi mellan webblÀsaren och servern.
Fördelar med att anvÀnda Import Maps i global utveckling
Fördelarna med att anamma Import Maps Àr mÄnga, sÀrskilt för internationella team och globalt distribuerade applikationer:
1. FörbÀttrad förutsÀgbarhet och kontroll
Import Maps eliminerar tvetydighet frÄn modulupplösning. Utvecklare vet alltid exakt var en modul kommer ifrÄn, oavsett deras lokala filstruktur eller pakethanterare. Detta Àr ovÀrderligt för stora team spridda över olika geografiska platser och tidszoner, vilket minskar "det fungerar pÄ min maskin"-syndromet.
2. FörbÀttrad prestanda
Genom att explicit definiera modulplatser kan du:
- Utnyttja CDN:er: Servera moduler frÄn Content Delivery Networks som Àr geografiskt nÀrmare dina anvÀndare, vilket minskar latensen.
- Cacha effektivt: Se till att webblÀsare och byggverktyg cachar moduler effektivt nÀr URL:erna Àr konsekventa.
- Minska bundler-overhead: I vissa fall, om alla beroenden serveras via CDN med Import Maps, kan du minska beroendet av stora, monolitiska buntar, vilket leder till snabbare initiala sidladdningar.
För en global SaaS-plattform kan servering av kÀrnbibliotek frÄn ett CDN mappat via Import Maps avsevÀrt förbÀttra anvÀndarupplevelsen för anvÀndare över hela vÀrlden.
3. Förenklad beroendehantering
Import Maps erbjuder ett deklarativt och centraliserat sÀtt att hantera beroenden. IstÀllet för att navigera i komplexa node_modules
-strukturer eller enbart förlita sig pÄ pakethanterarkonfigurationer, har du en enda sanningskÀlla för modulmappningar.
TÀnk pÄ ett projekt som anvÀnder olika UI-bibliotek, var och en med sin egen uppsÀttning beroenden. Import Maps lÄter dig mappa alla dessa bibliotek till antingen lokala sökvÀgar eller CDN-URL:er pÄ ett stÀlle, vilket gör uppdateringar eller byte av leverantör mycket enklare.
4. BĂ€ttre interoperabilitet
Import Maps kan överbrygga klyftan mellan olika modulsystem och utvecklingsmiljöer. Du kan mappa CommonJS-moduler för att konsumeras som ES-moduler, eller vice versa, med hjÀlp av verktyg som integreras med Import Maps. Detta Àr avgörande för att migrera Àldre kodbaser eller integrera tredjepartsmoduler som kanske inte Àr i ES-modulformat.
5. Strömlinjeformade utvecklingsflöden
Genom att minska komplexiteten i modulupplösning kan Import Maps leda till snabbare utvecklingscykler. Utvecklare spenderar mindre tid pÄ att felsöka importfel och mer tid pÄ att bygga funktioner. Detta Àr sÀrskilt fördelaktigt för agila team som arbetar under snÀva tidsfrister.
6. UnderlÀttar Micro-Frontend-arkitekturer
Micro-frontend-arkitekturer, dÀr en applikation bestÄr av oberoende, mindre frontends, drar stor nytta av Import Maps. Varje micro-frontend kan ha sin egen uppsÀttning beroenden, och Import Maps kan hantera hur dessa delade eller isolerade beroenden löses, vilket förhindrar versionskonflikter mellan olika micro-frontends.
FörestÀll dig en stor detaljhandelssajt dÀr produktkatalogen, varukorgen och anvÀndarkontosektionerna hanteras av separata team som micro-frontends. Var och en kan anvÀnda olika versioner av ett UI-ramverk. Import Maps kan hjÀlpa till att isolera dessa beroenden och sÀkerstÀlla att varukorgen inte av misstag konsumerar en version av UI-ramverket som Àr avsett för produktkatalogen.
Praktiska anvÀndningsfall och exempel
LÄt oss utforska nÄgra verkliga scenarier dÀr Import Maps kan tillÀmpas kraftfullt:
1. CDN-integration för global prestanda
Att mappa populÀra bibliotek till deras CDN-versioner Àr ett primÀrt anvÀndningsfall för prestandaoptimering, sÀrskilt för en global publik.
{
"imports": {
"react": "https://cdn.skypack.dev/react@18.2.0",
"react-dom": "https://cdn.skypack.dev/react-dom@18.2.0",
"vue": "https://cdn.jsdelivr.net/npm/vue@3.2.45/dist/vue.esm-browser.js"
}
}
Genom att anvÀnda tjÀnster som Skypack eller JSPM, som serverar moduler direkt i ES-modulformat, kan du sÀkerstÀlla att anvÀndare i olika regioner hÀmtar dessa kritiska beroenden frÄn en server nÀrmast dem.
2. Hantera lokala beroenden och alias
Import Maps kan ocksÄ förenkla lokal utveckling genom att tillhandahÄlla alias och mappa moduler inom ditt projekt.
{
"imports": {
"@/components/": "./src/components/",
"@/utils/": "./src/utils/",
"@/services/": "./src/services/"
}
}
Med denna karta skulle dina importer se mycket renare ut:
// IstÀllet för: import Button from './src/components/Button';
import Button from '@/components/Button';
// IstÀllet för: import { fetchData } from './src/services/api';
import { fetchData } from '@/services/api';
Detta förbÀttrar avsevÀrt kodens lÀsbarhet och underhÄllbarhet, sÀrskilt i projekt med djupa katalogstrukturer.
3. VersionslÄsning och kontroll
Medan pakethanterare hanterar versionering kan Import Maps ge ett extra lager av kontroll, sÀrskilt nÀr du behöver garantera att en specifik version anvÀnds över hela din applikation, och kringgÄ potentiella "hoisting"-problem i pakethanterare.
{
"imports": {
"lodash": "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.js"
}
}
Detta talar explicit om för webblÀsaren att alltid anvÀnda Lodash ES version 4.17.21, vilket sÀkerstÀller konsekvens.
4. ĂvergĂ„ng frĂ„n Ă€ldre kod
NÀr man migrerar ett projekt frÄn CommonJS till ES-moduler, eller nÀr man integrerar Àldre CommonJS-moduler i en ES-modul-kodbas, kan Import Maps fungera som en brygga.
Du kan anvÀnda ett verktyg som konverterar CommonJS-moduler till ES-moduler i farten och sedan anvÀnda en Import Map för att peka den "nakna" specificeraren till den konverterade modulen.
{
"imports": {
"legacy-module": "/converted-modules/legacy-module.js"
}
}
I din moderna ES-modul-kod:
import { oldFunction } from 'legacy-module';
Detta möjliggör en gradvis migrering utan omedelbara avbrott.
5. Integration med byggverktyg (t.ex. Vite)
Moderna byggverktyg integreras alltmer med Import Maps. Vite, till exempel, kan förhandsbunta beroenden med hjÀlp av Import Maps, vilket leder till snabbare serverstarter och byggtider.
NÀr Vite upptÀcker en <script type="importmap">
-tagg kan den anvÀnda dessa mappningar för att optimera sin beroendehantering. Detta innebÀr att dina Import Maps inte bara styr webblÀsarens upplösning utan ocksÄ pÄverkar din byggprocess, vilket skapar ett sammanhÀngande arbetsflöde.
Utmaningar och bÀsta praxis
Ăven om de Ă€r kraftfulla, Ă€r Import Maps inte utan sina utmaningar. Att anamma dem effektivt krĂ€ver noggrant övervĂ€gande:
- WebblÀsarstöd: Som nÀmnts, se till att du har en strategi för webblÀsare som inte har inbyggt stöd för Import Maps. Att anvÀnda
es-module-shims
Àr en vanlig lösning. - UnderhÄll: Att hÄlla din importkarta uppdaterad med ditt projekts beroenden Àr avgörande. Automation eller tydliga processer Àr nyckeln, sÀrskilt i större team.
- Komplexitet: För mycket enkla projekt kan Import Maps introducera onödig komplexitet. UtvÀrdera om fördelarna övervÀger overheaden.
- Felsökning: Ăven om de klargör upplösning, kan felsökning av problem som *faktiskt* uppstĂ„r ibland vara knepigt om sjĂ€lva kartan har fel.
BÀsta praxis för globala team:
- Etablera tydliga konventioner: Definiera en standard för hur importkartor struktureras och underhÄlls. Vem Àr ansvarig för uppdateringar?
- AnvÀnd externa filer: För större projekt, lagra importkartor i separata JSON-filer (t.ex.
import-maps.json
) för bÀttre organisation och cachning. - Utnyttja CDN för kÀrnbibliotek: Prioritera mappning av ofta anvÀnda, stabila bibliotek till CDN:er för globala prestandafördelar.
- Automatisera uppdateringar: Utforska verktyg eller skript som automatiskt kan uppdatera din importkarta nÀr beroenden Àndras, vilket minskar manuella fel.
- Dokumentera noggrant: Se till att alla teammedlemmar förstÄr hur importkartor anvÀnds i projektet och var man hittar konfigurationen.
- ĂvervĂ€g en monorepo-strategi: Om ditt globala team arbetar över flera relaterade projekt kan en monorepo-uppsĂ€ttning med en delad importkarta-strategi vara mycket effektiv.
- Testa över olika miljöer: Testa regelbundet din applikation i olika webblÀsarmiljöer och nÀtverksförhÄllanden för att sÀkerstÀlla konsekvent beteende.
Framtiden för JavaScript-modulupplösning
Import Maps representerar ett betydande steg mot ett mer förutsÀgbart och kontrollerbart ekosystem för JavaScript-moduler. Deras deklarativa natur och flexibilitet gör dem till en hörnsten för modern webbutveckling, sÀrskilt för storskaliga, globalt distribuerade applikationer.
I takt med att webblÀsarstödet mognar och integrationen med byggverktyg fördjupas kommer Import Maps sannolikt att bli en Ànnu mer integrerad del av JavaScript-utvecklarens verktygslÄda. De ger utvecklare möjlighet att göra explicita val om hur deras kod laddas och löses, vilket leder till bÀttre prestanda, underhÄllbarhet och en mer robust utvecklingsupplevelse för team över hela vÀrlden.
Genom att omfamna Import Maps anammar du inte bara ett nytt webblÀsar-API; du investerar i ett mer organiserat, effektivt och förutsÀgbart sÀtt att bygga och distribuera JavaScript-applikationer pÄ en global skala. De erbjuder en kraftfull lösning pÄ mÄnga lÄngvariga utmaningar inom beroendehantering och banar vÀg för renare kod, snabbare applikationer och mer samarbetande utvecklingsflöden över kontinenter.
Sammanfattning
JavaScript Import Maps ger ett avgörande kontrollskikt över modulupplösning och erbjuder betydande fördelar för modern webbutveckling, sÀrskilt i samband med globala team och distribuerade applikationer. FrÄn att förenkla beroendehantering och förbÀttra prestanda genom CDN-integration till att underlÀtta komplexa arkitekturer som micro-frontends, ger Import Maps utvecklare explicit kontroll.
Ăven om webblĂ€sarstöd och behovet av shims Ă€r viktiga övervĂ€ganden, gör fördelarna med förutsĂ€gbarhet, underhĂ„llbarhet och förbĂ€ttrad utvecklarupplevelse dem till en teknik vĂ€rd att utforska och anamma. Genom att förstĂ„ och implementera Import Maps effektivt kan du bygga mer motstĂ„ndskraftiga, högpresterande och hanterbara JavaScript-applikationer för din internationella publik.